<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>LODOP 打印调试页</title>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1"
    />
    <style>
      :root {
        color-scheme: light;
      }

      body {
        font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
        margin: 0;
        padding: 32px;
        background: #f5f7fb;
        color: #1f2433;
      }

      h1 {
        margin-top: 0;
        font-size: 26px;
        color: #2f3c58;
      }

      section {
        background: #fff;
        border-radius: 16px;
        box-shadow: 0 12px 30px rgba(56, 74, 156, 0.08);
        padding: 24px;
        margin-bottom: 32px;
      }

      .hint {
        color: #5c6786;
        line-height: 1.5;
        margin-bottom: 16px;
      }

      .panel {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
        gap: 16px;
        margin-bottom: 16px;
      }

      .panel label {
        font-size: 13px;
        display: flex;
        flex-direction: column;
        gap: 6px;
        color: #5b6583;
      }

      .panel input,
      .panel select {
        border: 1px solid #dfe4ef;
        border-radius: 6px;
        padding: 6px 8px;
        font-size: 14px;
        background: #fefefe;
      }

      .actions {
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        margin-bottom: 18px;
      }

      button {
        border: none;
        border-radius: 6px;
        padding: 10px 18px;
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
        color: #fff;
        background: linear-gradient(120deg, #556bfd, #7c86ff);
      }

      .secondary-btn {
        background: linear-gradient(120deg, #20c0d4, #1aa2d0);
      }

      .status {
        min-height: 20px;
        font-size: 13px;
        color: #5c688d;
        line-height: 20px;
      }

      .preview-wrapper {
        padding: 24px;
        border-radius: 16px;
        background: #fafbff;
        border: 1px dashed #d7dfee;
      }

      .print-container {
        margin: 0 auto;
        max-width: 180mm;
        background: #fff;
        border: 1px solid #d3d8e6;
        border-radius: 12px;
        padding: 16px 24px 24px;
        color: #1c2233;
      }

      .print-header {
        display: flex;
        justify-content: space-between;
        border-bottom: 1px solid #e0e5f2;
        padding-bottom: 12px;
        margin-bottom: 14px;
      }

      .info-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
        gap: 10px;
        margin-bottom: 16px;
        font-size: 13px;
        color: #4b5368;
      }

      .items-table {
        width: 100%;
        border-collapse: collapse;
        margin-bottom: 16px;
      }

      .items-table th,
      .items-table td {
        border: 1px solid #d7dfee;
        padding: 8px 10px;
        text-align: left;
        font-size: 13px;
      }

      .items-table th {
        background: #f1f4ff;
      }

      .thermal-section h2 {
        margin: 0 0 8px;
        color: #2f3e6a;
      }

      .thermal-preview {
        padding: 18px;
        border: 1px dashed #d7dfee;
        border-radius: 12px;
        background: #fafbff;
        display: flex;
        justify-content: center;
        margin-top: 12px;
      }

      .thermal-ticket {
        width: 76mm;
        background: #fff;
        border: 1px solid #d6dae4;
        border-radius: 8px;
        padding: 12px 10px 16px;
        font-size: 12px;
        color: #222;
      }

      .thermal-ticket h3 {
        margin: 0;
        font-size: 16px;
      }

      .thermal-ticket .meta {
        font-size: 12px;
        color: #5a6278;
        margin-bottom: 6px;
      }

      .thermal-ticket .divider {
        border-top: 1px dashed #c0c6d5;
        margin: 10px 0;
      }

      .thermal-ticket .row {
        display: flex;
        justify-content: space-between;
        margin: 4px 0;
      }

      .thermal-ticket .barcode {
        text-align: center;
        margin-top: 18px;
      }

      .thermal-actions {
        margin-top: 14px;
        display: flex;
        gap: 12px;
        flex-wrap: wrap;
      }

      .thermal-actions button {
        background: linear-gradient(120deg, #ff8c54, #ff6c5f);
      }

      .thermal-status {
        min-height: 20px;
        font-size: 13px;
        color: #5c688d;
        line-height: 20px;
      }
    </style>
  </head>
  <body>
    <h1>LODOP 打印调试页</h1>

    <section>
      <h2>A5 处方打印测试</h2>
      <p class="hint">
        可在下方调整页边距、内容宽度及 padding，然后点击按钮在 LODOP 中直接预览或打印。示例内容模拟 A5 处方。
      </p>

      <div class="panel">
        <label>
          纸张宽度
          <input id="page-width" value="148mm" />
        </label>
        <label>
          纸张高度
          <input id="page-height" value="210mm" />
        </label>
        <label>
          页边距（CSS 语法）
          <input id="margin" value="8mm 2mm 8mm 2mm" />
        </label>
        <label>
          内容最大宽度
          <input id="content-width" value="180mm" />
        </label>
        <label>
          内容内边距
          <input id="content-padding" value="2mm" />
        </label>
      </div>

      <div class="actions">
        <button id="preview-btn">LODOP 预览 (A5)</button>
        <button id="print-btn" class="secondary-btn">LODOP 直接打印 (A5)</button>
        <span id="status" class="status"></span>
      </div>

      <div class="preview-wrapper">
        <div id="print-area" class="print-container">
          <div class="print-header">
            <div>
              <div style="font-size: 20px; font-weight: 600">Zshan Clinic</div>
              <div style="font-size: 12px; color: #5a6378">
                Integrative Care &amp; Diagnostics
              </div>
            </div>
            <div style="text-align: right; font-size: 13px; color: #5c6883">
              <div>打印单号：ORD-2025-0106</div>
              <div>打印时间：2025-01-06 11:28</div>
            </div>
          </div>

          <div class="info-grid">
            <span>患者姓名：张珊珊</span>
            <span>性别/年龄：女 / 32 岁</span>
            <span>病历号：P-20231218-007</span>
            <span>医保卡号：8888 6666 5522</span>
          </div>

          <div class="info-grid">
            <span>医生：李安然 主治医师</span>
            <span>就诊科室：内分泌科</span>
            <span>就诊时间：2025-01-06 09:20</span>
            <span>打印人员：王静</span>
          </div>

          <table class="items-table">
            <thead>
              <tr>
                <th style="width: 40px">序</th>
                <th>项目名称</th>
                <th style="width: 60px">数量</th>
                <th style="width: 70px">单价</th>
                <th style="width: 90px">金额</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>1</td>
                <td>常规体检 / 全血细胞分析</td>
                <td>1</td>
                <td>¥85.00</td>
                <td>¥85.00</td>
              </tr>
              <tr>
                <td>2</td>
                <td>甲状腺功能五项（T3、T4、FT3…）</td>
                <td>1</td>
                <td>¥180.00</td>
                <td>¥180.00</td>
              </tr>
              <tr>
                <td>3</td>
                <td>口服葡萄糖耐量联检（空腹 + 2 小时）</td>
                <td>1</td>
                <td>¥260.00</td>
                <td>¥260.00</td>
              </tr>
              <tr>
                <td>4</td>
                <td>中医体质辨识评估</td>
                <td>1</td>
                <td>¥120.00</td>
                <td>¥120.00</td>
              </tr>
            </tbody>
            <tfoot>
              <tr>
                <td colspan="4" style="text-align: right">合计：</td>
                <td>¥645.00</td>
              </tr>
            </tfoot>
          </table>
          <div style="font-size: 12px; color: #5c688d">
            ☆ 建议打印宽度不超过 180mm，可通过上述控制面板调节。<br />
            ☆ 通过调整页边距可以验证内容是否被左右裁切。<br />
            ☆ 页面上的 `lodop-print-wrapper` 会自动限制 80mm 热敏宽度。
          </div>
        </div>
      </div>
    </section>

    <section class="thermal-section">
      <h2>热敏票据打印示例 (A80)</h2>
      <p class="hint">
        该示例固定 80mm 热敏纸参数：页宽 80mm、左右 0mm 外边距、内容 padding
        2mm。可直接点击按钮进行快速验证。
      </p>
      <div class="thermal-preview">
        <div id="thermal-print-area" class="thermal-ticket">
          <h3>智善诊所</h3>
          <div class="meta">热敏票据示例 · A80</div>
          <div class="divider"></div>
          <div>患者：张珊珊</div>
          <div>性别/年龄：女 / 32 岁</div>
          <div>挂号号：HS2025-1008</div>
          <div>医生：李安然</div>
          <div>科室：内分泌科</div>
          <div class="divider"></div>
          <div class="row">
            <span>就诊时间</span>
            <span>2025-01-06 09:20</span>
          </div>
          <div class="row">
            <span>类型</span>
            <span>专家号</span>
          </div>
          <div class="row">
            <span>费用</span>
            <span>¥30.00</span>
          </div>
          <div class="divider"></div>
          <div style="line-height: 1.5">
            地址：杭州市拱墅区祥符桥 1 号<br />
            电话：0571-1234567
          </div>
          <div class="barcode">
            <img
              src="https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=HS202501060920"
              alt="二维码"
            />
          </div>
        </div>
      </div>
      <div class="thermal-actions">
        <button id="thermal-preview-btn">LODOP 预览 (A80)</button>
        <button id="thermal-print-btn">LODOP 直接打印 (A80)</button>
        <span id="thermal-status" class="thermal-status"></span>
      </div>
    </section>

    <script src="http://localhost:8000/CLodopfuncs.js?priority=1"></script>
    <script src="http://localhost:18000/CLodopfuncs.js?priority=0"></script>
    <script>
      const PAGE_PRESETS = {
        A5: {
          width: '148mm',
          height: '210mm',
          name: 'A5',
          orientation: 'portrait',
          margin: '8mm 2mm 8mm 2mm',
          padding: '2mm',
        },
        A80: {
          width: '80mm',
          height: 'auto',
          name: 'A80',
          orientation: 'portrait',
          margin: '3mm 0mm 3mm 0mm',
          padding: '2mm',
        },
      };

      const DEFAULT_PAGE = PAGE_PRESETS.A5;
      const THERMAL_PAGE = PAGE_PRESETS.A80;

      const pageWidthInput = document.getElementById('page-width');
      const pageHeightInput = document.getElementById('page-height');
      const marginInput = document.getElementById('margin');
      const contentWidthInput = document.getElementById('content-width');
      const contentPaddingInput = document.getElementById('content-padding');
      const statusEl = document.getElementById('status');
      const printArea = document.getElementById('print-area');
      const thermalArea = document.getElementById('thermal-print-area');
      const thermalStatusEl = document.getElementById('thermal-status');

      const setStatus = (message, type = 'info') => {
        statusEl.textContent = message;
        statusEl.style.color =
          type === 'error' ? '#d93025' : type === 'success' ? '#1f9b53' : '#5c688d';
      };

      const setThermalStatus = (message, type = 'info') => {
        thermalStatusEl.textContent = message;
        thermalStatusEl.style.color =
          type === 'error' ? '#d93025' : type === 'success' ? '#1f9b53' : '#5c688d';
      };

      const convertSizeToLodopValue = (size) => {
        if (!size) return 0;
        if (typeof size === 'number') return size;
        const normalized = size.trim().toLowerCase();
        if (!normalized || normalized === 'auto') return 0;
        const mm = normalized.match(/^(\d+(?:\.\d+)?)mm$/);
        if (mm) return Math.round(parseFloat(mm[1]) * 10);
        const cm = normalized.match(/^(\d+(?:\.\d+)?)cm$/);
        if (cm) return Math.round(parseFloat(cm[1]) * 100);
        const inch = normalized.match(/^(\d+(?:\.\d+)?)in(ch|ches)?$/);
        if (inch) return Math.round(parseFloat(inch[1]) * 254);
        const px = normalized.match(/^(\d+(?:\.\d+)?)px$/);
        if (px) return Math.round((parseFloat(px[1]) * 254) / 96);
        const numeric = Number(normalized);
        return Number.isFinite(numeric) ? numeric : 0;
      };

      const convertCssToMm = (value) => convertSizeToLodopValue(value) / 10;

      const parseMarginValues = (margin) => {
        const defaultValue = '0mm';
        if (!margin) {
          return {
            top: defaultValue,
            right: defaultValue,
            bottom: defaultValue,
            left: defaultValue,
          };
        }
        const normalized = margin
          .split(/\s+/)
          .map((value) => value.trim())
          .filter(Boolean);
        const normalizeValue = (value) => {
          if (!value || value.toLowerCase() === 'auto') {
            return defaultValue;
          }
          return value === '0' ? defaultValue : value;
        };
        const [top, right, bottom, left] = (() => {
          switch (normalized.length) {
            case 1:
              return [normalized[0], normalized[0], normalized[0], normalized[0]];
            case 2:
              return [normalized[0], normalized[1], normalized[0], normalized[1]];
            case 3:
              return [normalized[0], normalized[1], normalized[2], normalized[1]];
            default:
              return [
                normalized[0],
                normalized[1],
                normalized[2],
                normalized[3] ?? normalized[1],
              ];
          }
        })();

        return {
          top: normalizeValue(top),
          right: normalizeValue(right),
          bottom: normalizeValue(bottom),
          left: normalizeValue(left),
        };
      };

      const computeSafeContentWidth = () => {
        const pageWidthValue = pageWidthInput.value || DEFAULT_PAGE.width || '';
        const pageWidthMm = convertCssToMm(pageWidthValue);
        if (!pageWidthMm) return null;

        const { left, right } = parseMarginValues(marginInput.value);
        const leftMm = convertCssToMm(left);
        const rightMm = convertCssToMm(right);
        const paddingMm = convertCssToMm(contentPaddingInput.value);

        const safe = pageWidthMm - leftMm - rightMm - paddingMm * 2;
        return safe > 0 ? `${Math.max(20, safe).toFixed(1)}mm` : null;
      };

      const applyPreviewWidth = () => {
        const widthValue = (contentWidthInput.value || '').trim();
        printArea.style.maxWidth = widthValue || '';
      };

      const setAutoContentWidth = (force = false) => {
        if (!force && contentWidthInput.dataset.manual === 'true') {
          return;
        }
        const available = computeSafeContentWidth();
        if (!available) return;
        contentWidthInput.value = available;
        applyPreviewWidth();
      };

      const applyPagePreset = () => {
        pageWidthInput.value = DEFAULT_PAGE.width;
        pageHeightInput.value = DEFAULT_PAGE.height;
        marginInput.value = DEFAULT_PAGE.margin;
        contentPaddingInput.value = DEFAULT_PAGE.padding;
      };

      applyPagePreset();
      setAutoContentWidth(true);
      applyPreviewWidth();

      contentWidthInput.addEventListener('input', () => {
        contentWidthInput.dataset.manual = 'true';
        applyPreviewWidth();
      });

      marginInput.addEventListener('input', () => setAutoContentWidth(true));
      contentPaddingInput.addEventListener('input', () => setAutoContentWidth(true));
      pageWidthInput.addEventListener('input', () => setAutoContentWidth(true));

      const convertDocument = (contentHtml, padding, width) => {
        const widthStyles = width
          ? `
            .print-wrapper {
              width: ${width};
              max-width: ${width};
              margin: 0 auto;
            }
          `
          : '';

        return `
        <!DOCTYPE html>
        <html lang="zh-CN">
          <head>
            <meta charset="UTF-8" />
            <title>LODOP 测试</title>
            <style>
              * { box-sizing: border-box; }
              body {
                margin: 0;
                padding: ${padding};
                font-family: 'Microsoft YaHei','PingFang SC',sans-serif;
                -webkit-print-color-adjust: exact;
                print-color-adjust: exact;
              }
              .print-wrapper {
                border: 1px solid #cfd6e7;
                border-radius: 6px;
                padding: 12px 16px 18px;
                background: #fff;
              }
              .print-wrapper * { max-width: 100%; }
              .print-wrapper table { width: 100%; border-collapse: collapse; }
              ${widthStyles}
            </style>
          </head>
          <body>
            <div class="print-wrapper">${contentHtml}</div>
          </body>
        </html>
      `;
      };

      const getLodopInstance = () => {
        try {
          if (typeof getCLodop === 'function') {
            const instance = getCLodop();
            if (instance) return instance;
          }
        } catch (error) {
          console.error('getCLodop 调用失败:', error);
        }

        if (window.CLODOP) {
          return window.CLODOP;
        }
        setStatus('未检测到 LODOP，请确认本地服务已启动。', 'error');
        setThermalStatus('未检测到 LODOP，请确认本地服务已启动。', 'error');
        return null;
      };

      function handlePrint(isPreview) {
        const lodop = getLodopInstance();
        if (!lodop) return;

        const pageWidth = pageWidthInput.value.trim() || DEFAULT_PAGE.width;
        const pageHeight = pageHeightInput.value.trim() || DEFAULT_PAGE.height;
        const marginValues = parseMarginValues(marginInput.value);
        const contentPadding = contentPaddingInput.value || DEFAULT_PAGE.padding;
        const contentWidth = contentWidthInput.value || '180mm';

        const documentHtml = convertDocument(
          printArea.outerHTML,
          contentPadding,
          contentWidth
        );

        try {
          lodop.PRINT_INIT('A5 处方测试');
          if (typeof lodop.SET_PRINT_MODE === 'function') {
            lodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', true);
            lodop.SET_PRINT_MODE('FULL_WIDTH_FOR_OVERFLOW', true);
          }

          lodop.SET_PRINT_PAGESIZE(
            1,
            convertSizeToLodopValue(pageWidth),
            convertSizeToLodopValue(pageHeight),
            PAGE_PRESETS.A5.name
          );

          lodop.ADD_PRINT_HTM(
            marginValues.top,
            marginValues.left,
            `RightMargin:${marginValues.right}`,
            `BottomMargin:${marginValues.bottom}`,
            documentHtml
          );

          if (isPreview) {
            if (typeof lodop.PREVIEW === 'function') {
              lodop.PREVIEW();
              setStatus('已打开 LODOP 预览窗口。', 'success');
              return;
            }
            setStatus('LODOP 不支持预览，尝试直接打印。', 'error');
          }

          if (typeof lodop.PRINT === 'function') {
            const result = lodop.PRINT();
            setStatus(result ? '已发送打印任务。' : 'LODOP 打印失败。', result ? 'success' : 'error');
          }
        } catch (error) {
          console.error('LODOP 打印失败:', error);
          setStatus('LODOP 打印失败，请查看控制台日志。', 'error');
        }
      }

      function handleThermalPrint(isPreview) {
        const lodop = getLodopInstance();
        if (!lodop) return;

        const marginValues = parseMarginValues(THERMAL_PAGE.margin);
        const documentHtml = convertDocument(
          thermalArea.outerHTML,
          THERMAL_PAGE.padding,
          '76mm'
        );

        try {
          lodop.PRINT_INIT('A80 热敏票据');
          if (typeof lodop.SET_PRINT_MODE === 'function') {
            lodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', true);
            lodop.SET_PRINT_MODE('FULL_WIDTH_FOR_OVERFLOW', true);
          }

          lodop.SET_PRINT_PAGESIZE(
            1,
            convertSizeToLodopValue(THERMAL_PAGE.width),
            0,
            THERMAL_PAGE.name
          );

          lodop.ADD_PRINT_HTM(
            marginValues.top,
            marginValues.left,
            `RightMargin:${marginValues.right}`,
            `BottomMargin:${marginValues.bottom}`,
            documentHtml
          );

          if (isPreview) {
            if (typeof lodop.PREVIEW === 'function') {
              lodop.PREVIEW();
              setThermalStatus('已打开 LODOP 预览窗口。', 'success');
              return;
            }
            setThermalStatus('LODOP 不支持预览，尝试直接打印。', 'error');
          }

          if (typeof lodop.PRINT === 'function') {
            const result = lodop.PRINT();
            setThermalStatus(
              result ? '已发送打印任务。' : 'LODOP 打印失败。',
              result ? 'success' : 'error'
            );
          }
        } catch (error) {
          console.error('LODOP 打印失败:', error);
          setThermalStatus('LODOP 打印失败，请查看控制台日志。', 'error');
        }
      }

      document.getElementById('preview-btn').addEventListener('click', () => handlePrint(true));
      document.getElementById('print-btn').addEventListener('click', () => handlePrint(false));
      document
        .getElementById('thermal-preview-btn')
        .addEventListener('click', () => handleThermalPrint(true));
      document
        .getElementById('thermal-print-btn')
        .addEventListener('click', () => handleThermalPrint(false));

      setStatus('等待操作。');
      setThermalStatus('等待操作。');
    </script>
  </body>
</html>
